From 3be130f83911170df86f5d37df523fe64d3623c6 Mon Sep 17 00:00:00 2001 From: "kfraser@dhcp93.uk.xensource.com" Date: Mon, 19 Jun 2006 10:18:39 +0100 Subject: [PATCH] [XEN] Fix ptwr_do_page_fault() after new more permissive entry conditions to that function. Now that we no longer check PGERR_page_present (i.e., that fault is a protection fault), because it's not safe to do so, we can enter ptwr_do_page_fault() with a not-present L2 page directory. We then crash on our first access to the linear_l2_table. This patch fixes all unprotected accesses to the linear_l2_table to correctly handle faults. Also move setup of globally-visible ptwr info record to end of ptwr_do_page_fault(). We don't want the info picked up by reentrant invocations of the page-fault handler. Signed-off-by: Keir Fraser --- xen/arch/x86/mm.c | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 17f12ec3fb..612a255fdc 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -3495,11 +3495,13 @@ int ptwr_do_page_fault(struct domain *d, unsigned long addr, /* * Attempt to read the PTE that maps the VA being accessed. By checking for * PDE validity in the L2 we avoid many expensive fixups in __get_user(). - * NB. The L2 entry cannot be detached as the caller already checked that. + * NB. The L2 entry cannot be detached due to existing ptwr work: the + * caller already checked that. */ - if ( !(l2e_get_flags(__linear_l2_table[l2_linear_offset(addr)]) & - _PAGE_PRESENT) || - __copy_from_user(&pte,&linear_pg_table[l1_linear_offset(addr)], + pl2e = &__linear_l2_table[l2_linear_offset(addr)]; + if ( __copy_from_user(&l2e, pl2e, sizeof(l2e)) || + !(l2e_get_flags(l2e) & _PAGE_PRESENT) || + __copy_from_user(&pte, &linear_pg_table[l1_linear_offset(addr)], sizeof(pte)) ) { return 0; @@ -3617,18 +3619,16 @@ int ptwr_do_page_fault(struct domain *d, unsigned long addr, "pfn %lx\n", PTWR_PRINT_WHICH, addr, l2_idx << L2_PAGETABLE_SHIFT, pfn); - d->arch.ptwr[which].l1va = addr | 1; - d->arch.ptwr[which].l2_idx = l2_idx; - d->arch.ptwr[which].vcpu = current; - -#ifdef PERF_ARRAYS - d->arch.ptwr[which].eip = regs->eip; -#endif - /* For safety, disconnect the L1 p.t. page from current space. */ if ( which == PTWR_PT_ACTIVE ) { - l2e_remove_flags(*pl2e, _PAGE_PRESENT); + l2e_remove_flags(l2e, _PAGE_PRESENT); + if ( unlikely(__copy_to_user(pl2e, &l2e, sizeof(l2e))) ) + { + MEM_LOG("ptwr: Could not unhook l2e at %p", pl2e); + domain_crash(d); + return 0; + } flush_tlb_mask(d->domain_dirty_cpumask); } @@ -3642,14 +3642,24 @@ int ptwr_do_page_fault(struct domain *d, unsigned long addr, if ( unlikely(__put_user(pte.l1, &linear_pg_table[l1_linear_offset(addr)].l1)) ) { - MEM_LOG("ptwr: Could not update pte at %p", (unsigned long *) + MEM_LOG("ptwr: Could not update pte at %p", &linear_pg_table[l1_linear_offset(addr)]); - /* Toss the writable pagetable state and crash. */ - d->arch.ptwr[which].l1va = 0; domain_crash(d); return 0; } + /* + * Now record the writable pagetable state *after* any accesses that can + * cause a recursive page fault (i.e., those via the *_user() accessors). + * Otherwise we can enter ptwr_flush() with half-done ptwr state. + */ + d->arch.ptwr[which].l1va = addr | 1; + d->arch.ptwr[which].l2_idx = l2_idx; + d->arch.ptwr[which].vcpu = current; +#ifdef PERF_ARRAYS + d->arch.ptwr[which].eip = regs->eip; +#endif + return EXCRET_fault_fixed; emulate: -- 2.30.2